package com.test.rpc;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.util.Map;
import java.util.Set;

public class MyFeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    private ResourceLoader resourceLoader;
    private Environment environment;
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 创建扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(MyFeignClient.class);
        scanner.addIncludeFilter(annotationTypeFilter);
        // 查找扫描包集合
        Map<String, Object> attrs = importingClassMetadata.getAnnotationAttributes(EnableMyFeignClients.class.getName());
        if (attrs == null) {
            return;
        }
        String[] packages = (String[]) attrs.get("value");
        if (packages.length == 0) {
            packages = getDefaultPackages(importingClassMetadata);
        }
        for (String p : packages) {
            // 扫描
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(p);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition bd = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = bd.getMetadata();
                    // 注册FactoryBean
                    registerFeignClient(registry, annotationMetadata);
                }
            }
        }
    }
	// 注册MyFeignClientFactoryBean
    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata) {
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(MyFeignClient.class.getName());
        if (annotationAttributes == null)
            return;
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyFeignClientFactoryBean.class);
        definitionBuilder.addPropertyValue("type", annotationMetadata.getClassName());
        definitionBuilder.addPropertyValue("name", annotationAttributes.get("value"));
        AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
        registry.registerBeanDefinition(annotationMetadata.getClassName(), beanDefinition);
    }

    // 取注解所在类所在包作为扫描路径
    private String[] getDefaultPackages(AnnotationMetadata importingClassMetadata) {
        String className = importingClassMetadata.getClassName();
        int lastDotIndex = className.lastIndexOf(".");
        return lastDotIndex != -1 ? new String[]{className.substring(0, lastDotIndex)} : new String[]{};
    }

    private ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                // 目标beanDefinition必须是顶层类（不依赖与其他类isIndependent）且不能是注解
                return beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation();
            }
        };
    }
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}